<?php
/**
 * PHP Framework Domain Model Entity class
 * This class represents a generic entity and implements database interaction
 * methods
 * Must be inherited to implement generic and specific entities
 *
 * TODO: Fix SQL-injections here
 *
 * @author Andrew <andrew@vitche.com>
 * @author Frozen Rain <frozenrain@mail.ru>
 **/

class Entity extends Object {

    /**
     * @var array Stores entity fields and field values of generic entities
     * @access private
     **/
    private $_fields = array();

    /**
     * Constructs a domain model entity
     *
     * @access public
     * @param array $arFields An array of entitie's fields and field values
     **/
    public function __construct($arFields = null) {

        if (isset($arFields)) {
            $this->_fields = $arFields;
        }

        $this->type = new EntityType($this->_fields);
    }

    /**
     * Returns an entity field value
     * Returns null when the field is not available
     *
     * @param string $name Entity field name
     * @return string Requested entity field value
     * @access public
     **/
    public function __get($name) {

        if (isset($this->_fields[$name])) {
            return $this->_fields[$name];
        }

        return null;
    }

    /**
     * Handles undeclared entity fields of generic entities
     *
     * @param string $name Entity field name
     * @param mixed $value Entity field value
     * @access public
     **/
    public function __set($name, $value) {
        $this->_fields[$name] = $value;
    }

    /**
     * Encodes all entity fields to the HTML-friendly format
     **/
    public function htmlEncode() {

        foreach ($this->_fields as $key => $value) {
            if (!is_object($value) && !is_array($value)) {
                $this->_fields[$key] = htmlentities($value, ENT_QUOTES, "UTF-8");
            }
        }
    }

    /**
     * Creates a generic entity
     * TODO: Add support for field values types
     * @return boolean True on success, false otherwise
     * @access public
     **/
    public function insert() {

        $strTable = $this->getTable();
        $strFieldNames = implode(', ', array_keys($this->_fields));
        $strFieldValues = implode('\', \'', array_values($this->_fields));
        $strSQLQuery = 'INSERT INTO ' . $strTable . ' (' . $strFieldNames . ') VALUES (\'' . $strFieldValues . '\')';
        return Storage::executeSQL($strSQLQuery);
    }

    public function delete($id = null) {

        if (isset($this->_fields['id'])) {
            $id = $this->_fields['id'];
        }
        $strTable = $this->getTable();
        $strSQLQuery = 'DELETE FROM ' . $strTable . ' WHERE 1 = 1 ';
        if (isset($id)) {
            $strSQLQuery .= 'AND id = ' . $id;
        }
        return Storage::executeSQL($strSQLQuery);
    }

    /**
     * Loads a single entity instance from the session array
     * @return Entity
     *
     * TODO: Replace with a universal implementation
     **/
    public function loadFromSession() {

        if (isset($_SESSION['Framework.DomainModel'])) {
            if (isset($_SESSION['Framework.DomainModel'][$this->getName()])) {
                return $_SESSION['Framework.DomainModel'][$this->getName()];
            }
        }
        return null;
    }

    /**
     * TODO: Replace with a universal implementation
     **/
    public function removeFromSession() {
        if (isset($_SESSION['Framework.DomainModel'])) {
            if (isset($_SESSION['Framework.DomainModel'][$this->getName()])) {
                unset($_SESSION['Framework.DomainModel'][$this->getName()]);
            }
        }
    }

    /**
     * Saves a single entity instance to the session array
     * 
     * TODO: Replace with a universal implementation
     **/
    public function saveToSession() {

        if (!isset($_SESSION['Framework.DomainModel'])) {
            $_SESSION['Framework.DomainModel'] = array();
        }
        $_SESSION['Framework.DomainModel'][$this->getName()] = $this;
    }

    /**
     * Loads a list of entities matching the given condition
     * @param array $arConditions
     * @return array An array of resulting entities
     **/
    public function select($arConditions = null) {

        $strTable = $this->getTable();
        $strSQLQuery = 'SELECT * FROM ' . $strTable . ' WHERE 1 = 1';

        if (!isset($arConditions)) {

            $arConditions = $this->_fields;
        }

        if (isset($arConditions)) {
            foreach ($arConditions as $key => $value) {

                if ('NULL' == $value) {

                    $strSQLQuery .= ' AND ' . $key . ' IS NULL';
                }
                else if ('EntityCondition' == get_class($value)) {

                        $strSQLQuery .= ' AND ' . $value->key . ' ' . $value->condition . '\'' . $value->value . '\'';
                    } else {

                        $strSQLQuery .= ' AND ' . $key . ' =\'' . $value . '\'';
                    }
            }
            $strSQLQuery = trim($strSQLQuery, ' ,');
        }
        $arRes = Storage::executeSQL($strSQLQuery);
        $arData = array();
        if ($arRes) {
            $arRes = $arRes->getRows();
            foreach ($arRes as $value) {
                $arFields = array();
                foreach($value as $key => $field) {
                    if (is_string($key)) {
                        if (null != $field) {
                            $arFields[$key] = $field;
                        }
                    }
                }
                $Entity = $this->getName();
                $Entity = new $Entity($arFields);
                $arData[] = $Entity;
            }
        }
        return $arData;
    }

    public function toArray() {
        return $this->_fields;
    }

    public function update() {

        $strTable = $this->getTable();
        $strSQLQuery = 'UPDATE ' . $strTable . ' SET ';
        foreach ($this->_fields as $key => $value) {
            $strSQLQuery .= $key . ' = \'' . $value . '\', ';
        }
        $strSQLQuery = trim($strSQLQuery, ', ');
        $strSQLQuery .= ' WHERE 1 = 1 ';
        if (isset($this->_fields['id'])) {
            $strSQLQuery .= 'AND id = ' . $this->_fields['id'];
        }
        return Storage::executeSQL($strSQLQuery);
    }

    private function getName() {

        return get_class($this);
    }

    /**
     * Builds a table name of a generic entity
     * TODO: Add suport for complex word forms
     * @return string Table name
     * @access private
     **/
    private function getTable() {

        $strTable = $this->getName();
        $strTable = NamingConventionMapper::windowsAPIToOracle($strTable);
        $strTable = NamingConventionMapper::toPlural($strTable);
        return $strTable;
    }
}
?>
